package com.fanghuaiming.tsec.pay.controller;

import com.alibaba.fastjson.JSONObject;
import com.fanghuaiming.tsec.pay.common.Jms;
import com.fanghuaiming.tsec.pay.config.vo.TsecPayVo;
import com.fanghuaiming.tsec.pay.dao.model.TsecPay;
import com.fanghuaiming.tsec.pay.mqtransaction.PayTransactionListener;
import com.fanghuaiming.tsec.pay.service.PayService;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/****
 * @description: 支付服务接口
 * @version:1.0.0
 * @author fanghuaiming
 * @data Created in 2020/11/27 下午2:00
 *
 */
@Slf4j
@RestController("pay")
@RequestMapping("/pay")
public class PayController {


    /**
     * 发送配置
     */
    @Autowired
    private Jms jms;

    /**
     * 支付service
     */
    @Autowired
    private PayService payService;

    @Autowired
    private PayTransactionListener payTransactionListener;

    /**
     * 配置多线程提高发送效率
     */
    private ExecutorService executorService = new ThreadPoolExecutor(2, 5, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
        @Override
        public Thread newThread(Runnable runnable) {
            Thread thread = new Thread(runnable);
            thread.setName("tsec-pay-transaction-msg-check-thread");
            return thread;
        }
    });

    /**
     * @Description: 更新支付状态,模拟支付
     *
     * @param:
     * @return:
     * @auther: fanghuaiming
     * @date: 2020/11/30 下午6:28
     * @version:1.0.0
     */
    @SuppressWarnings("all")
    @PostMapping("/updatePayStatus")
    public String updatePayStatus(@RequestBody TsecPayVo tsecPayVo) throws MQClientException {
        TransactionMQProducer producer = null;
        SendResult sendResult = null;
        try {
            log.info("==========最终一致性-支付微服务开始创建订单==========");
            //测试使用uuid,正式使用可以用雪花算法id,可以进行消息的幂等,在消息进行回查的时候,在表中添加属性做为二级索引,进行索引覆盖查询
            String uuid = UUID.randomUUID().toString().replace("_", "");
            //封装消息
            JSONObject msgJson = new JSONObject();
            msgJson.put("money", tsecPayVo.getMoney());
            msgJson.put("userId", tsecPayVo.getUserId());
            msgJson.put("goods", tsecPayVo.getGoods());
            msgJson.put("uuid", uuid);
            msgJson.put("time", new Date().toString());
            String jsonString = msgJson.toJSONString();
            //封装本地事务业务需要的对象
            TsecPay tsecPay = new TsecPay();
            tsecPay.setPayStatus(tsecPayVo.getPayStatus());
            tsecPay.setId(tsecPayVo.getPayId());
            tsecPay.setUuid(uuid);
            //封装消息实体
            Message message = new Message(jms.getPayTopic(), jms.getPayTag(), uuid,jsonString.getBytes());
            //创建事务消息监听器
            producer = new TransactionMQProducer(jms.getProducerGroup());
            // 添加服务器地址
            producer.setNamesrvAddr(jms.getNameServer());
            //失败就不发
            //producer.setRetryTimesWhenSendAsyncFailed(0);
            producer.setExecutorService(executorService);
            // 添加事务监听器
            producer.setTransactionListener(payTransactionListener);
            producer.start();
            log.info("==========最终一致性-支付微服务开始发送 【 half消息 】==========");
            //发送消息 用 sendMessageInTransaction  第一个参数可以理解成消费方需要的参数 第二个参数可以理解成消费方不需要 本地事务需要的参数
            sendResult = producer.sendMessageInTransaction(message,tsecPay);
            if (SendStatus.SEND_OK == sendResult.getSendStatus()) {
                producer.shutdown();
                return "支付half消息发送成功";
            }
        } catch (MQClientException e) {
            e.printStackTrace();
            if(null != producer){
                producer.shutdown();
            }
        }
        return "支付half消息发送失败";
    }
}
